package Thread;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description: 计时器的模拟实现
 */
//创建一个类，表示一个任务
class TimerTask implements Comparable<TimerTask>{
    //任务中需要包括任务要求和时间
    //1.描述一个任务
    private Runnable runnable;
    //2.说明时间
    private long delay;

    //此处传入的时间应该是相对时间，而我们要将任务时间与系统时间比较的话，就要转成绝对时间
    public TimerTask(Runnable runnable, long time) {
        this.runnable = runnable;
        this.delay = time + System.currentTimeMillis();
    }

    public void run() {
        runnable.run();
    }

    public long getDelay() {
        return delay;
    }

    @Override
    public int compareTo(TimerTask o) {
        // 为什么这里实例 o 可以直接得到 delay 属性
        // 因为是在自己类中调用的本身的属性，this 和 o 并没有区别
        return (int) (this.delay - o.delay);
    }
}

class MyTimer {
    //首先应该创建一个管理任务的数据结构，时间最靠前的任务先执行
    //因此选择堆，并且要保证线程安全
    //由于采用了堆保存，并且我们应该按照时间维护一个小根堆，因此任务类必须实现Comparable接口
    private PriorityBlockingQueue<TimerTask> queue = new PriorityBlockingQueue<>();

    //提供一个可以传任务和时间的方法，并且把任务传到队列中
    public void schedule (Runnable runnable, long delay) {
        TimerTask task = new TimerTask(runnable, delay);
        queue.put(task);
        //每当插入了一个新的任务，都要进行唤醒操作
        synchronized (locker) {
            locker.notify();
        }
    }

    private static Object locker = new Object();

    // 创建一个MyTimer实例，那么就应该时刻等待任务执行
    // 因此在构造方法中，直接创建一个线程来执行到达时间的任务
    public MyTimer() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                //此时如果没有任何操作，就这样一直while(true)不断的循环
                //是极其浪费资源的，尤其是CPU，称为”忙等“
                //为了避免这种情况，我们采用wait()方法
                //等时间到了或有新的任务插入进来，才进行唤醒
                while(true) {
                    try {
                        TimerTask task = queue.take();
                        long curTime = System.currentTimeMillis();
                        if(curTime < task.getDelay()) {
                            queue.put(task);
                            synchronized (locker) {
                                locker.wait(task.getDelay() - curTime);
                            }
                            continue;
                        }
                        task.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }
            }
        };
        //不要忘记执行
        thread.start();
    }

}

public class ThreadDemo28 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 1000);
        System.out.println("main");
    }
}
